package com.hn.springframework.context;

import com.hn.springframework.annotation.HNAutowired;
import com.hn.springframework.annotation.HNController;
import com.hn.springframework.annotation.HNService;
import com.hn.springframework.aop.HNAopProxy;
import com.hn.springframework.aop.HNCglibAopProxy;
import com.hn.springframework.aop.HNJdkDynamicAopProxy;
import com.hn.springframework.aop.config.HNAopconfig;
import com.hn.springframework.aop.support.HNAdvisedSupport;
import com.hn.springframework.beans.HNBeanWrapper;
import com.hn.springframework.beans.config.HNBeanDefinition;
import com.hn.springframework.beans.support.HNBeanDefinitionReader;
import com.hn.springframework.beans.support.HNDefaultListableBeanFactory;
import com.hn.springframework.core.HNBeanFactory;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 *  IOC 核心容器
 */
@Slf4j
public class HNApplicationContext extends HNDefaultListableBeanFactory implements HNBeanFactory {

    private String[] configLoactions;
    private HNBeanDefinitionReader reader;

    // 单例的IOC容器缓存
    private Map<String,Object> factoryBeanObjectCache = new ConcurrentHashMap<String, Object>();
    // 通用的IOC容器
    private Map<String, HNBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<String, HNBeanWrapper>();

    public HNApplicationContext(String... configLoactions) {
        this.configLoactions = configLoactions;
        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *  1、定位配置文件
     *  2、加载配置文件，扫描相关类，封装成 BeanDefinitions
     *  3、将 BeanDefinitions 注册到容器中（伪 IOC 容器）
     *  4、非延时加载类的初始化
     * @throws Exception
     */
    @Override
    public void refresh() throws Exception {
        this.reader = new HNBeanDefinitionReader(this.configLoactions);
        List<HNBeanDefinition> beanDefinitions = this.reader.loadBeanDefinitions();
        doRegisterBeanDefinition(beanDefinitions);
        doAutowired();
    }

    @Override
    public Object getBean(Class<?> beanClass) throws Exception {
        return getBean(beanClass.getName());
    }

    /**
     *  1、读取 BeanDefinition 中的信息
     *  2、根据 BeanDefinition 创建一个实例，包装成一个 BeanWrapper
     *  3、依赖注入
     *  4、前置处理器和后置处理器对bean实例进行增强
     * @param beanBame
     * @return
     * @throws Exception
     */
    @Override
    public Object getBean(String beanName) throws Exception {
        HNBeanDefinition beanDefinition = super.beanDefinitionMap.get(beanName);
        if (beanDefinition == null) { return null; }
        Object instance = instantiateBean(beanName, beanDefinition);
        HNBeanWrapper beanWrapper = new HNBeanWrapper(instance);
        // TODO Spring中什么时候put的没找到
        this.factoryBeanInstanceCache.put(beanName,beanWrapper);
        populateBean(beanName, beanWrapper);
        // TODO 前置处理器、后置处理器

        return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
    }

    /**
     * 实例化一个 bean
     * @param beanName
     * @param beanDefinition
     * @return
     */
    private Object instantiateBean(String beanName, HNBeanDefinition beanDefinition) {
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            if (this.factoryBeanObjectCache.containsKey(className)) {
                instance = this.factoryBeanObjectCache.get(className);
            } else {
                Class<?> clazz = Class.forName(className);
                instance = clazz.newInstance();

                HNAdvisedSupport config = instantionAopConfig();
                config.setTargetClass(clazz);
                config.setTarget(instance);
                //符合PointCut的规则的话，闯将代理对象
                if(config.pointCutMatch()) {
                    instance = createProxy(config).getProxy();
                }

                this.factoryBeanObjectCache.put(className, instance);
                this.factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(), instance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    private HNAopProxy createProxy(HNAdvisedSupport config) {
        if (config.getTargetClass().getInterfaces().length > 0) {
            return new HNJdkDynamicAopProxy(config);
        }
        return new HNCglibAopProxy();
    }

    private HNAdvisedSupport instantionAopConfig() {
        HNAopconfig config = new HNAopconfig();
        config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
        return new HNAdvisedSupport(config);
    }

    private void populateBean(String beanName, HNBeanWrapper beanWrapper) {
        Class<?> clazz = beanWrapper.getWrappedClass();
        if (!clazz.isAnnotationPresent(HNController.class) && !clazz.isAnnotationPresent(HNService.class)) {
            return;
        }
        Object instance = beanWrapper.getWrappedInstance();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(HNAutowired.class)) { continue; }
            HNAutowired autowired = field.getAnnotation(HNAutowired.class);
            String autowiredBeanName = autowired.value().trim();
            if ("".equals(autowiredBeanName)) {
                autowiredBeanName = field.getType().getName();
            }

            // 强制访问
            field.setAccessible(true);
            try {
                // TODO 可能不存在，
                //  Spring 中使用了缓存，循环依赖的时候，第一个 bean1 会先放入缓存，
                //  发现 bean1 依赖 bean2，就去缓存中找 bean2，未找到则初始化 bean2，
                //  发现 bean2 依赖 bean1，去缓存中找 bean1，找到并注入，然后返回 bean2 给 bean1 注入
                if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){ continue; }
                field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private void doRegisterBeanDefinition(List<HNBeanDefinition> beanDefinitions) throws Exception {
        for (HNBeanDefinition beanDefinition : beanDefinitions) {
            // TODO 这里不是应该覆盖吗
            if (super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The “" + beanDefinition.getFactoryBeanName() + "” is exists!!");
            }
            log.info("register bean:" + beanDefinition.getFactoryBeanName() + "=" + beanDefinition.getBeanClassName());
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
        }
    }

    private void doAutowired() {
        for (Map.Entry<String, HNBeanDefinition> beanDefinitionEntry : super.beanDefinitionMap.entrySet()) {
            String beanFactoryName = beanDefinitionEntry.getKey();
            if (!beanDefinitionEntry.getValue().isLazyInit()) {
                try {
                    getBean(beanFactoryName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public Properties getConfig(){
        return this.reader.getConfig();
    }
}
